/**
*		@file Next.cpp
*		@brief Next class
*
*		@author Lan Ziquan
*
*		@date 2/11/2012
*/

#include "Next.h"

vector<StmtSet> Next::next_table_;
vector<StmtSet> Next::next_table_t_;
vector<StmtSet>	Next::next_adv_table_;
vector<StmtSet>	Next::next_adv_cache_table_;

StmtSet			Next::next_has_s0_;
StmtSet			Next::next_has_0s_;
bool			Next::next_has_00_;

void Next::RebuildNext()
{
	next_table_.resize(StmtTable::GetSize());
	next_table_t_.resize(StmtTable::GetSize());
	next_adv_table_.resize(StmtTable::GetSize());
	next_adv_cache_table_.resize(StmtTable::GetSize());
	for( int i = 0; i < StmtTable::GetSize(); i++ )
	{
		next_table_[i].resize(StmtTable::GetSize());
		next_table_t_[i].resize(StmtTable::GetSize());
		next_adv_table_[i].resize(StmtTable::GetSize());
		next_adv_cache_table_[i].resize(StmtTable::GetSize());
	}

	for( int i = 0; i < StmtTable::GetSize(); i++ )
	{
		if( StmtTable::GetStmtGNode(i+1)->next_a != NULL )
			next_table_[i][StmtTable::GetStmtGNode(i+1)->next_a->stmt_num - 1] = true;
		if( StmtTable::GetStmtGNode(i+1)->next_b != NULL )
			next_table_[i][StmtTable::GetStmtGNode(i+1)->next_b->stmt_num - 1] = true;
	}
	
	for( int i = 0; i < StmtTable::GetSize(); i++ )
		for( int j = 0; j < StmtTable::GetSize(); j++ )
			next_table_t_[i][j] = next_table_[j][i];

	next_has_s0_.resize(StmtTable::GetSize());
	next_has_0s_.resize(StmtTable::GetSize());
	next_has_00_ = false;
	return;
}

void Next::CompleteNext()
{
	for( int i = 0; i < StmtTable::GetSize(); i++ )	next_has_s0_[i] = IsAny<StmtSet>(next_table_[i]);
	for( int i = 0; i < StmtTable::GetSize(); i++ )	next_has_0s_[i] = IsAny<StmtSet>(next_table_t_[i]);
	next_has_00_ = IsAny<StmtSet>(next_has_s0_);
	return;
}

StmtSet& Next::GetNextRight(StmtNum stmt_num_left)
{
	return next_table_[stmt_num_left - 1];
}

StmtSet& Next::GetNextLeft(StmtNum stmt_num_Right)
{
	return next_table_t_[stmt_num_Right - 1];
}

//bool Next::HasNextAdvRight(StmtNum stmt_num_left)
//{
//	if( stmt_num_left > StmtTable::GetSize() )
//		return false;
//	return StmtTable::GetStmtGNode(stmt_num_left)->next_a != NULL;
//}
//
//bool Next::HasNextAdvLeft(StmtNum stmt_num_right)
//{
//	if( stmt_num_right > StmtTable::GetSize() )
//		return false;
//	// case 1 WHILE_TYPE
//	if( StmtTable::GetStmtTNode(stmt_num_right)->type == WHILE_TYPE )
//		return true;
//	// case 2 not WHILE_TYPE and first progline in some procedure
//	if( StmtTable::GetStmtTNode(stmt_num_right)->parent->parent->type == PROC_TYPE &&
//		ProcTable::GetStmtNumStart(StmtTable::GetStmtTNode(stmt_num_right)->parent->parent->value) == stmt_num_right )
//		return false;
//	// case 3 not first progline in some procedure
//	return true;
//}

bool Next::IsNext(StmtNum stmt_num_left, StmtNum stmt_num_right)
{
	if( stmt_num_left > StmtTable::GetSize() || stmt_num_right > StmtTable::GetSize() )
		return false;
	if( stmt_num_left == 0 )
	{
		if( stmt_num_right == 0 )
			return next_has_00_;
		else
			return next_has_0s_[stmt_num_right - 1];
	}
	else
	{
		if( stmt_num_right == 0 )
			return next_has_s0_[stmt_num_left - 1];
		else
			return next_table_[stmt_num_left - 1][stmt_num_right - 1];
	}
}

bool Next::IsNextAdv(StmtNum stmt_num_left, StmtNum stmt_num_right)
{
	if( stmt_num_left > StmtTable::GetSize() || stmt_num_right > StmtTable::GetSize() )
		return false;
	if( stmt_num_left == 0 )
	{
		if( stmt_num_right == 0 )
			return next_has_00_;
		else
			return next_has_0s_[stmt_num_right - 1];
	}
	else
	{
		if( stmt_num_right == 0 )
			return next_has_s0_[stmt_num_left - 1];
		else
		{
			// case 1 cached
			bool b_read_in;
			if( ReadAndReturnNextAdv(stmt_num_left, stmt_num_right, b_read_in) )
				return b_read_in;
			// case 2 non-cached
			// case 2.1 left is the SAME as right
			if( stmt_num_left == stmt_num_right )
			{
				// case 2.1.1 left(right) is WHILE_TYPE
				if( StmtTable::GetStmtTNode(stmt_num_left)->type == WHILE_TYPE )
					return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, true);
				// case 2.1.2 find WHILE parent*
				for( int i = 0; i < StmtTable::GetSizeWhile(); i++ )
				{
					StmtNum stmt_num_while = StmtTable::GetWhileTNode(i+1)->stmt_num;
					// case 2.1.2.1 already impossible to find a WHILE parent*
					if( stmt_num_while > stmt_num_left )
						return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, false);
					// case 2.1.2.2 has WHILE parent*
					if( Parent::IsParentAdv(stmt_num_while, stmt_num_left) )
						return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, true);
				}
				// case 2.1.2.3 cannot find WHILE parent*
				return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, false);
			}
			// case 2.2 left is AFTER right
			if( stmt_num_left > stmt_num_right )
			{
				// case 2.2.1 right if WHILE_TYPE and Parent*(right, left)
				if( StmtTable::GetStmtTNode(stmt_num_right)->type == WHILE_TYPE &&
					Parent::IsParentAdv(stmt_num_right, stmt_num_left) )
					return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, true);
				// case 2.2.2 find common WHILE parent*
				for( int i = 0; i < StmtTable::GetSizeWhile(); i++ )
				{
					StmtNum stmt_num_while = StmtTable::GetWhileTNode(i+1)->stmt_num;
					// case 2.2.2.1 already impossible to find a common WHILE parent*
					if( stmt_num_while > stmt_num_right )
						return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, false);
					// case 2.2.2.2 has common WHILE parent*
					if( Parent::IsParentAdv(stmt_num_while, stmt_num_left) &&
						Parent::IsParentAdv(stmt_num_while, stmt_num_right))
						return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, true);
				}
				// case 2.2.2.3 cannot find common WHILE parent*
				return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, false);
			}
			// case 2.3 left is BEFORE right
			else
			{
				// case 2.3.1 not in the same procedure
				for( int i = 0; i < ProcTable::GetSize(); i++ )
				{
					if( ProcTable::GetStmtNumStart(i+1) <= stmt_num_left &&
						ProcTable::GetStmtNumEnd(i+1) >= stmt_num_left )
					{
						if( ProcTable::GetStmtNumStart(i+1) > stmt_num_right ||
							ProcTable::GetStmtNumEnd(i+1) < stmt_num_right )
							return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, false);
						else break;
					}	
				}
				// case 2.3.2 in the same procedure
				// case 2.3.2.1 have a common WHILE parent*
				for( int i = 0; i < StmtTable::GetSizeWhile(); i++ )
				{
					StmtNum stmt_num_while = StmtTable::GetWhileTNode(i+1)->stmt_num;
					if( Parent::IsParentAdv(stmt_num_while, stmt_num_left) &&
						Parent::IsParentAdv(stmt_num_while, stmt_num_right))
						return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, true);
				}
				// case 2.3.2.2 not have a common WHILE parent*
				for( int i = StmtTable::GetSizeIf(); i > 0; i-- )
				{
					StmtNum stmt_num_if = StmtTable::GetIfTNode(i)->stmt_num;
					// case 2.3.2.2.1 impossible to be a common IF parent*
					if( stmt_num_if >= stmt_num_left )
						continue;
					// case 2.3.2.2.2 has common IF parent*
					if( Parent::IsParentAdv(stmt_num_if, stmt_num_left) &&
						Parent::IsParentAdv(stmt_num_if, stmt_num_right))
					{
						StmtNum stmt_num_else_separator = StmtTable::GetStmtGNode(stmt_num_if)->next_b->stmt_num;
						// case 2.3.2.2.2.1 left and right on the same branch
						if( stmt_num_left >= stmt_num_else_separator ||
							stmt_num_right < stmt_num_else_separator)
							return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, true);
						// case 2.3.2.2.2.2 left and right on different branches
						return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, false);
					}
				}
				// case 2.3.2.2.3 cannot find common IF parent*
				return WriteAndReturnNextAdv(stmt_num_left, stmt_num_right, true);
			}
		}
	}	
}

bool Next::ReadAndReturnNextAdv(StmtNum stmt_num_left, StmtNum stmt_num_right, bool& b_read_in)
{
	QueryThread::GetLockNext();
	bool result;
	if( next_adv_cache_table_[stmt_num_left - 1][stmt_num_right - 1] )
	{
		b_read_in = next_adv_table_[stmt_num_left - 1][stmt_num_right - 1];
		result = true;
	}
	else {result = false;}
	QueryThread::ReleaseLockNext();
	return result;
}

bool Next::WriteAndReturnNextAdv(StmtNum stmt_num_left, StmtNum stmt_num_right, bool b_set_to)
{
	QueryThread::GetLockNext();
	next_adv_table_[stmt_num_left - 1][stmt_num_right - 1] = b_set_to;
	next_adv_cache_table_[stmt_num_left - 1][stmt_num_right - 1] = true;
	QueryThread::ReleaseLockNext();
	return b_set_to;
}
